home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-09-27 | 7.6 KB | 226 lines | [TEXT/CCL ] |
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; sndMgr.Lisp
- ;;
- ;; Copyright © 1990 Michael S. Engber
- ;; All Rights Reserved
- ;;
- ;; Sound Manager access from LISP
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (require 'traps)
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Sound Manager definitions (missing from Records.Lisp)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (eval-when (compile load eval)
-
- (defrecord (SndCommand :pointer)
- (cmd :integer)
- (param1 :integer)
- (param2 :longint)
- )
-
- (defrecord (SndChannel :pointer)
- (nextChan :pointer)
- (firstMode :pointer)
- (callBack :pointer)
- (userInfo :longint)
- (wait :longint) ;Time
- (cmdInProgress SndCommand)
- (flags :integer)
- (qLength :integer)
- (qHead :integer)
- (qTail :integer)
- (queue :longint) ;array [0..stdQLength-1] of SndCommand
- )
-
- ;;; sound commands
- (defconstant $nullCmd 0)
- (defconstant $initCmd 1)
- (defconstant $freeCmd 2)
- (defconstant $quietCmd 3)
- (defconstant $flushCmd 4)
- (defconstant $waitCmd 10)
- (defconstant $pauseCmd 11)
- (defconstant $resumeCmd 12)
- (defconstant $callBackCmd 13)
- (defconstant $syncCmd 14)
- (defconstant $emptyCmd 15)
- (defconstant $tickleCmd 20)
- (defconstant $requestNextCmd 21)
- (defconstant $howOftenCmd 22)
- (defconstant $wakeUpCmd 23)
- (defconstant $availableCmd 24)
- (defconstant $versionCmd 25)
- (defconstant $scaleCmd 30)
- (defconstant $tempoCmd 31)
- (defconstant $noteCmd 40)
- (defconstant $restCmd 41)
- (defconstant $freqCmd 42)
- (defconstant $ampCmd 43)
- (defconstant $timbreCmd 44)
- (defconstant $waveTableCmd 60)
- (defconstant $phaseCmd 61)
- (defconstant $soundCmd 80)
- (defconstant $bufferCmd 81)
- (defconstant $rateCmd 82)
- (defconstant $continueCmd 83)
- (defconstant $midiDataCmd 100)
-
- )
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defvar *snd-channel_p* nil "pointer to currently opened sound channel")
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defmacro with-sound (sndSpec &rest forms)
- "(sndSpec) -forms-
- Protective 'with' wrapper for using sounds."
- (unless (listp sndSpec) (error "bad options"))
- `(unwind-protect (progn (snd-open ,(first sndSpec)) ,@forms) (snd-close)))
-
- (defun snd-halt ()
- "void
- Halts any sound in progress & closes the channel."
- (when *snd-channel_p*
- (snd-command-immediate $quietCmd 0 0)
- (snd-command-immediate $flushCmd 0 0)
- (snd-close)))
-
- (defun snd-p ()
- "void
- Returns whether a sound (played with snd-open) is currently playing."
- (when *snd-channel_p*
- (null (zerop (rref *snd-channel_p* :SndChannel.userInfo)))))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; snd-open & snd-close open a sound channel, play a sound, and then close
- ;; the sound channel. Since only one sound channel can be active at any time,
- ;; it is important that every call to snd-open be followed by a call to
- ;; snd-close (or snd-halt) as soon as possible. While a sound channel is open,
- ;; no other sounds (like system beeps) can play.
- ;;
- ;; The with-sound macro safely takes care of all this for you. When control
- ;; leaves the body, either normally or abnormally, the sound channel is closed.
-
- (defun snd-open (sndSpec &key (async t))
- "sndSpec &key (async t)
- Plays the specified sound (asynchronously by default)
- sndSpec is either a resource number or name of a 'snd ' resource."
- (let ((snd_h (get-snd sndSpec)))
- (when snd_h
- (cond
- (async
- (snd-open-channel)
- (rset *snd-channel_p* :SndChannel.userInfo -1)
- (_SndPlay :ptr *snd-channel_p* :ptr snd_h :word -1 :word)
- (snd-command $callBackCmd 0 0))
- (t
- (snd-close)
- (_SndPlay :ptr nil :ptr snd_h :word 0 :word))))))
-
- (defun snd-close ()
- "void
- Cleans up after sound finishes."
- (snd-close-channel))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;; Determining whether a sound is still playing (snd-p) is done using a
- ;; Sound Manager call back routines. Before playing a sound, the userInfo
- ;; field of the sound channel is set to -1. When the sound completes, the
- ;; call back routine is called and it sets userInfo to zero. snd-p simply
- ;; checks the value of userInfo.
- ;;
- ;; Since the call back routine is called at interrupt time, there are several
- ;; restrictions on it (see Sound Manager chapter of IM) which MACL's defpascal
- ;; mechanism does not obey. So it was written in C. The compiled code is small
- ;; enough that we can just copy its machine code into memory when a sound channel
- ;; is created (avoiding loading CODE resources or external function calls)
- ;;
- ;; #include <SoundMgr.h>
- ;;
- ;; pascal void main (SndChannelPtr theChan, SndCommand* theCmd){
- ;; theChan->userInfo = 0L;
- ;; }
- ;;
- ;;
-
- (defvar *snd-call-back-mcode* "600E0000434F444501F400000000000041FAFFEE4E714E71600000024E560000206E000C42A8000C4E5E205F4FEF00084ED04D41494E20202020"
- "machine code (hex) for call back routine")
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (defun get-snd (sndSpec)
- "sndSpec
- Returns a handle to the specified 'snd ' resource.
- sndSpec is either a resource number or name of a 'snd ' resource."
- (typecase sndSpec
- (fixnum (_GetResource :ostype "snd " :word sndSpec :ptr))
- (string (with-pstrs ((name sndSpec))
- (_GetNamedResource :ostype "snd " :ptr name :ptr)))
- (otherwise (error "bad resource specification [~S]" sndSpec))))
-
- (defun snd-open-channel ()
- "void
- Opens a new channel for sound play."
- (when *snd-channel_p* (snd-close-channel))
- (%stack-block ((channel_p 4))
-
- ;; pass nil for the channel_p so the Sound Mangager will allocate space
- (%put-ptr channel_p nil)
-
- ;; stuff machine code for call back routine into memory
- (let ((call-back-ptr (_NewPtr :d0 (/ (length *snd-call-back-mcode*) 2) :a0)))
- (with-pstrs ((p *snd-call-back-mcode*))
- (_StuffHex :ptr call-back-ptr :ptr p))
-
- (if (zerop (_SndNewChanne :ptr channel_p :word 0 :long 0 :ptr call-back-ptr :word))
- (setf *snd-channel_p* (%get-ptr channel_p))
- (error "unable to allocate new sound channel.")))))
-
- (defun snd-close-channel ()
- (when *snd-channel_p*
- (_DisposPtr :a0 (rref *snd-channel_p* :SndChannel.callBack) :d0)
- (_SndDisposeChannel :ptr *snd-channel_p* :word 0 :word)
- (setf *snd-channel_p* nil)))
-
- (defun snd-command (cmd param1 param2)
- "cmd param1 param2
- Adds the specified command to the sound channel's queue."
- (when *snd-channel_p*
- (rlet ((cmd_p :SndCommand :cmd cmd :param1 param1 :param2 param2))
- (_SndDoCommand :ptr *snd-channel_p* :ptr cmd_p :word 0 :word))))
-
- (defun snd-command-immediate (cmd param1 param2)
- "cmd param1 param2
- Sends the sound channel the specified command to immediately execute."
- (when *snd-channel_p*
- (rlet ((cmd_p :SndCommand :cmd cmd :param1 param1 :param2 param2))
- (_SndDoImmediat :ptr *snd-channel_p* :ptr cmd_p :word))))
-
-
- #|
-
- test code
-
- This plays a sound asychronously. During play it checks to see if
- the shift key is pressed - if so it halts the sound immediatlely.
- It uses the 'snd ' resource id = 1, the standard system beep.
-
- (with-sound (1)
- (loop (when (or (shift-key-p) (null (snd-p))) (snd-halt) (return))))
-
- The standard system beep is so short that the above code isn't too
- exciting as is. You may want to try some of the longer system beeps
- like Clink-Klang (id = 2) if you have them installed. Or better yet,
- open a sound resource file of your own with this code.
-
- (with-pstrs ((res_file "insert path to your sound file"))
- (_openresfile :ptr res_file :word))
-
- |#
-